package envelopes

import (
	"context"
	"errors"
	acservice "gitee.com/wm1998/account/services"
	"gitee.com/wm1998/infra/base"
	"gitee.com/wm1998/resk/services"
	log "github.com/sirupsen/logrus"
	"github.com/tietang/dbx"
)

// 处理过期红包业务领域层结构体
type ExpiredEnvelopeDomain struct {
	expiredGoods []RedEnvelopeGoods // 已过期的红包切片
	offset       int                // 查询过期红包的起始位置
}

const (
	pageSize = 100 // 查询过期红包，每次查询的行数为100
)

// 分页查询是否有过期红包，返回：true-有过期红包，并将分页查询的起始位置设置为下一页的查询起始位置；false-没有过期红包
func (e *ExpiredEnvelopeDomain) Next() (ok bool) {
	base.Tx(func(runner *dbx.TxRunner) error {
		dao := RedEnvelopeGoodsDao{runner: runner}           // 构建DAO对象
		e.expiredGoods = dao.FindExpired(e.offset, pageSize) // 调用DAO的分页查询过期的所有红包，分页，limit,offset size
		if len(e.expiredGoods) > 0 {
			e.offset += len(e.expiredGoods) // 将分页查询的起始位置设置为下一页的查询起始位置
			ok = true
		}
		return nil
	})
	return ok
}

// 处理过期红包：遍历每个过期红包，然后为每个过期红包发起退款流程
func (e *ExpiredEnvelopeDomain) Expired() (err error) {
	for e.Next() {
		for _, g := range e.expiredGoods {
			log.Debugf("过期红包退款开始：%+v", g)
			err := e.ExpiredOne(g)
			if err != nil {
				log.Error(err)
			}
			log.Debugf("过期红包退款结束：%+v", g)
		}
	}
	return err
}

// 发起过期红包的退款流程
func (e *ExpiredEnvelopeDomain) ExpiredOne(goods RedEnvelopeGoods) (err error) {
	// 创建一个退款订单（红包商品）
	refund := goods
	refund.OrderType = services.OrderTypeRefund
	//refund.RemainAmount = goods.RemainAmount.Mul(decimal.NewFromFloat(-1))
	//refund.RemainQuantity = -goods.RemainQuantity
	refund.Status = services.OrderExpired // 设置订单（红包商品）的状态为：过期失效
	refund.PayStatus = services.Refunding // 设置订单（红包商品）的支付状态为：退款中
	refund.OriginEnvelopeNo = goods.EnvelopeNo
	refund.EnvelopeNo = ""

	domain := goodsDomain{RedEnvelopeGoods: refund}
	domain.createEnvelopeNo()

	err = base.Tx(func(runner *dbx.TxRunner) error {
		//【因涉及多个事务操作，所以需要使用一个dbx.TxRunner（通过context来传递这个TxRunner）】
		txCtx := base.WithValueContext(context.Background(), runner)
		_, err := domain.Save(txCtx) // 【1.保存订单（红包商品）】
		if err != nil {
			return errors.New("创建退款订单（红包商品）失败" + err.Error())
		}
		// 修改原订单的订单状态
		dao := RedEnvelopeGoodsDao{runner: runner}
		_, err = dao.UpdateOrderStatus(goods.EnvelopeNo, services.OrderExpired) // 【2.更改原订单（红包商品）】的订单状态为：过期
		if err != nil {
			return errors.New("更新原订单状态失败" + err.Error())
		}
		return nil
	})
	if err != nil {
		return
	}

	// 【3.执行退款流程，即：调用资金账户接口的转账方法】
	systemAccount := base.GetSystemAccount()
	account := acservice.GetAccountService().GetEnvelopeAccountByUserId(goods.UserId)
	if account == nil {
		return errors.New("没有找到该用户的红包资金账户:" + goods.UserId)
	}
	body := acservice.TradeParticipator{
		Username:  systemAccount.Username,
		UserId:    systemAccount.UserId,
		AccountNo: systemAccount.AccountNo,
	}
	target := acservice.TradeParticipator{
		Username:  account.Username,
		UserId:    account.UserId,
		AccountNo: account.AccountNo,
	}
	transfer := acservice.AccountTransferDTO{
		TradeBody:   body,
		TradeTarget: target,
		TradeNo:     refund.EnvelopeNo,
		Amount:      goods.RemainAmount,
		ChangeType:  acservice.EnvelopExpiredRefund,
		ChangeFlag:  acservice.FlagTransferIn,
		Decs:        "红包过期退款:" + goods.EnvelopeNo,
	}
	status, err := acservice.GetAccountService().Transfer(transfer) // 【3.执行退款流程，即：调用资金账户接口的转账方法】
	if status != acservice.TransferedStatusSuccess {
		return err
	}

	err = base.Tx(func(runner *dbx.TxRunner) error {
		dao := RedEnvelopeGoodsDao{runner: runner}
		// 【4.修改原订单（红包商品）状态】
		rows, err := dao.UpdateOrderStatus(goods.EnvelopeNo, services.OrderExpiredRefundSuccessful)
		if err != nil || rows == 0 {
			return errors.New("更新原订单状态失败")
		}
		// 【5.修改退款订单（红包商品）状态】
		rows, err = dao.UpdateOrderStatus(refund.EnvelopeNo, services.OrderExpiredRefundSuccessful)
		if err != nil || rows == 0 {
			return errors.New("更新退款订单状态失败")
		}
		return nil
	})
	if err != nil {
		log.Error(err)
		return err
	}
	return nil
}

//把测试和debug作为作业留给同学们，
// 过程中遇到问题和bug可以留言和发起问题讨论
